<template>
  <view class="classify__wrap">
    <view class="classify__search-box">
      <view class="classify__search-inner">
        <icon type="search" color="#909399" :size="14"></icon>
        <input
            class="classify__nav-bar-input"
            placeholder-class="classify__placeholder-class"
            type="text"
            :placeholder="placeholder"
            @input="filterOptions"
            v-model="searchText"
        />
        <!--#ifndef MP-ALIPAY-->
        <icon type="clear" color="#909399" class="classify__clear-search" :size="14" v-show="searchText" @click.native="clearSearchText"></icon>
        <!--#endif-->

        <!--#ifdef MP-ALIPAY-->
        <view class="classify__clear-search" v-show="searchText" @click="clearSearchText">×</view>
        <!--#endif-->

      </view>

    </view>
    <view class="classify__menu-wrap" v-show="!!isSearchMatch">
      <scroll-view scroll-y scroll-with-animation class="classify__tab-view classify__menu-scroll-view" :scroll-top="scrollTop"
                   :scroll-into-view="itemId">
        <view v-for="(item,index) in list" :key="index"
              class="classify__tab-item" :class="[current === index ? 'classify__tab-item-active' : '']"
              @tap.stop="switchMenu(index)"
              v-show="!item.noSearchMatch"
        >
          <text class="classify__line-1">{{item[menuLabelKeyName]}}</text>
          <view class="classify__badge" v-if="!!item.count">{{ item.count | getCount }}</view>
        </view>
      </scroll-view>
      <scroll-view :scroll-top="scrollRightTop" scroll-y scroll-with-animation class="classify__right-box" @scroll="rightScroll">
        <view class="classify__page-view">
          <view class="classify__class-item"
                v-for="(item , index) in list"
                :key="index"
                :id="'item' + index"
                v-show="!item.noSearchMatch"
          >
            <view class="classify__item-title">
              <text>{{item[menuLabelKeyName]}}</text>
            </view>
            <view class="classify__item-container">
              <view class="classify__thumb-box" v-for="(item1, index1) in item[childrenKeyName]" :key="index1"
                    hover-class="classify__click-class"
                    @click="rightOnChange(item1, item)"
                    v-show="!item1.noSearchMatch"
              >
                <slot name="classify-kids" :row="item1">
                  <view v-show="mode === 'multiple'">
                    <i class="classify__iconfont classify__icon-check classify__selected-icon" v-show="selectedValues.includes(item1[childValueKeyName])"></i>
                    <i class="classify__iconfont classify__icon-weixuanzhong classify__selected-icon" v-show="!selectedValues.includes(item1[childValueKeyName])"></i>
                  </view>
                  <image class="classify__item-menu-image" :src="item1[iconKeyName]" mode=""></image>
                  <view class="classify__item-menu-name">{{item1[childLabelKeyName]}}</view>
                </slot>
              </view>
            </view>
          </view>
        </view>
      </scroll-view>
    </view>

    <slot name="empty" v-if="!isSearchMatch">
      <view class="classify__empty">
        <i class="classify__iconfont classify__icon-meiyouxiangguan classify__empty-icon"></i>
      </view>
    </slot>

    <ShoppingCart
        v-show="isSearchMatch"
        :list.sync="list"
        :selectedRows.sync="selectedRows"
        :selectedValues.sync="selectedValues"
        :childrenKeyName="childrenKeyName"
        :childLabelKeyName="childLabelKeyName"
        :childValueKeyName="childValueKeyName"
        :iconKeyName="iconKeyName"
        @change="confirm"
    >
      <template v-slot:classify-kids="{row}">
        <slot name="popup-classify-kids" :row="row">
          <slot name="classify-kids" :row="row" />
        </slot>
      </template>
    </ShoppingCart>
  </view>
</template>
<script>
import ShoppingCart from '../ShoppingCart'
export default {
  props: {
    // 分类数据源
    classifyData: {
      type: Array,
      default: () => []
    },

    // 模式，单选、多选
    mode: {
      type: String,
      default: ''
    },

    // 菜单要显示的字段
    menuLabelKeyName: {
      type: String,
      default: 'name'
    },

    // 菜单主键键名
    menuValueKeyName: {
      type: String,
      default: 'id'
    },

    // 右侧子分类字段
    childrenKeyName: {
      type: String,
      default: 'children'
    },

    // 右侧子分类要显示的字段
    childLabelKeyName: {
      type: String,
      default: 'name'
    },

    // 右侧子分类主键键名
    childValueKeyName: {
      type: String,
      default: 'id'
    },

    // 右侧子分类icon键名
    iconKeyName: {
      type: String,
      default: 'icon'
    },

    // 最多可选择数量
    max: {
      type: [Number, String],
      default: 999
    },

    // 多选时，超过max值时的提示语国际化
    lang: {
      type: String,
      default: 'zh'
    },

    // placeholder
    placeholder: {
      type: String,
      default: '搜索'
    },

  },
  components: { ShoppingCart },
  data() {
    return {
      list: [],
      scrollTop: 0, //tab标题的滚动条位置
      oldScrollTop: 0,
      current: 0, // 预设当前项的值
      menuHeight: 0, // 左边菜单的高度
      menuItemHeight: 0, // 左边菜单item的高度
      itemId: '', // 栏目右边scroll-view用于滚动的id
      menuItemPos: [],
      arr: [],
      scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
      timer: null, // 定时器
      selectedValues: [], // 选中的数据values
      selectedRows: [], // 选择的数据rows，带有parent
      searchText: '', // 搜索字
      isSearchMatch: true, // 是否有匹配
      filterOptions: this.debounce(this.search, 0)
    }
  },
  filters: {
    getCount(val) {
      return val > 99 ? '99+' : val
    }
  },
  computed: {},
  watch: {
    classifyData: { // 监听一个对象，对应值为对象
      handler (curVal) {
        const list = JSON.parse(JSON.stringify(curVal))
        list.forEach((item, index) => {
          item.count = 0
          item.checked = false
          item.noSearchMatch = false
          item.classifyIndex = index
        })
        this.list = list
      },
      deep: true, // 对象内部的属性监听，深度侦听
      immediate: true // 声明的时候，就立即执行handler方法
    }
  },
  onLoad() {},
  created() {},
  mounted() {
    this.getMenuItemTop()
  },
  onReady() {
    this.getMenuItemTop()
  },
  methods: {
    // 点击左边的栏目切换
    async switchMenu(index) {
      if(this.arr.length === 0) {
        await this.getMenuItemTop();
      }
      if (index === this.current) return;
      this.scrollRightTop = this.oldScrollTop;
      this.$nextTick(function(){
        this.scrollRightTop = this.arr[index];
        this.current = index;
        this.leftMenuStatus(index);
      })
    },
    // 获取一个目标元素的高度
    getElRect(elClass, dataVal) {
      new Promise((resolve) => {
        const query = uni.createSelectorQuery().in(this);
        query.select('.' + elClass).fields({
          size: true
        }, res => {
          // 如果节点尚未生成，res值为null，循环调用执行
          if (!res) {
            setTimeout(() => {
              this.getElRect(elClass);
            }, 10);
            return;
          }
          this[dataVal] = res.height;
          resolve();
        }).exec();
      })
    },
    // 观测元素相交状态
    async observer() {
      this.list.map((val, index) => {
        let observer = uni.createIntersectionObserver(this);
        // 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
        // 如果跟.right-box底部相交，就动态设置左边栏目的活动状态
        observer.relativeTo('.classify__right-box', {
          top: 0
        }).observe('#item' + index, res => {
          if (res.intersectionRatio > 0) {
            let id = res.id.substring(4);
            this.leftMenuStatus(id);
          }
        })
      })
    },
    // 设置左边菜单的滚动状态
    async leftMenuStatus(index) {
      this.current = index;
      // 如果为0，意味着尚未初始化
      if (this.menuHeight === 0 || this.menuItemHeight === 0) {
        await this.getElRect('classify__menu-scroll-view', 'menuHeight');
        await this.getElRect('classify__tab-item', 'menuItemHeight');
      }
      // 将菜单活动item垂直居中
      this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
    },
    // 获取右边菜单每个item到顶部的距离
    getMenuItemTop() {
      new Promise(resolve => {
        let selectorQuery = uni.createSelectorQuery().in(this);
        selectorQuery.selectAll('.classify__class-item').boundingClientRect((rects) => {
          // 如果节点尚未生成，rects值为[](因为用selectAll，所以返回的是数组)，循环调用执行
          if(!rects.length) {
            setTimeout(() => {
              this.getMenuItemTop();
            }, 10);
            return ;
          }
          rects.forEach((rect) => {
            // 这里减去rects[0].top，是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
            this.arr.push(rect.top - rects[0].top);
            resolve();
          })
        }).exec()
      })
    },
    // 右边菜单滚动
    async rightScroll(e) {
      this.oldScrollTop = e.detail.scrollTop;
      if(this.arr.length === 0) {
        await this.getMenuItemTop();
      }
      if(this.timer) return ;
      if(!this.menuHeight) {
        await this.getElRect('classify__menu-scroll-view', 'menuHeight');
      }
      const that = this
      setTimeout(() => { // 节流
        that.timer = null;
        // scrollHeight为右边菜单四等分点位置
        let scrollHeight = e.detail.scrollTop + that.menuHeight / 4;
        for (let i = 0; i < that.arr.length; i++) {
          let height1 = that.arr[i];
          let height2 = that.arr[i + 1];
          // 如果不存在height2，意味着数据循环已经到了最后一个，设置左边菜单为最后一项即可
          if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
            that.leftMenuStatus(i);
            return ;
          }
        }
      }, 10)
    },

    // 选择小类别
    rightOnChange(row, parent) {
      if (this.mode === 'multiple') {
        const aa = this.childValueKeyName
        const index = this.selectedValues.findIndex(item => {
          return item === row[aa]
        })
        if (!row.checked) {
          // 处理最大值情况
          if (this.selectedValues.length === Number(this.max)) {
            let text = ''
            switch (this.lang) {
              case 'zh': // 中文
                text = `最多可选${this.max}个`
                break
              case 'en': // 英文
                text = `A maximum of ${this.max} can be selected`
                break
              case 'es': // 西班牙文
                text = `Hasta ${this.max} Opciones`
                break
              case 'ja': // 日文
                text = `${this.max}つまで選べる`
                break
              default:
                text = `最多可选${this.max}个`
                break
            }
            uni.showToast({
              title: text,
              icon: 'none'
            })
            return
          }
          row.checked = true
          parent.count++
          row.parentClassifyIndex = parent.classifyIndex
          this.selectedValues.push(row[this.childValueKeyName])
          this.selectedRows.push(row)
        } else {
          row.checked = false
          parent.count--
          this.selectedValues.splice(index, 1)
          this.selectedRows.splice(index, 1)
        }
      } else {
        row.checked = !row.checked;
        this.$emit('change', row[this.childValueKeyName], row)
      }
    },

    // 多选确定
    confirm(selectedValues, selectedRows) {
      this.$emit('change', selectedValues, selectedRows)
    },

    // 搜索
    search() {
      if (this.searchText) {
        let count = 0
        this.list.forEach((item, index) => {
          let searchMatchCount = 0
          item[this.childrenKeyName].forEach((item1, index1) => {
            if (item1[this.childLabelKeyName].toLocaleLowerCase().includes(this.searchText.toLocaleLowerCase())) {
              searchMatchCount++
              item1.noSearchMatch = false
            } else {
              item1.noSearchMatch = true
              this.$set(this.list[index][this.childrenKeyName][index1], 'noSearchMatch', true)
            }
          })
          if (searchMatchCount === 0) {
            item.noSearchMatch = true
          } else {
            item.noSearchMatch = false
            count++
          }
        })
        this.isSearchMatch = !!count

      } else {
        this.list.forEach((item) => {
          item.noSearchMatch = false
          item[this.childrenKeyName].forEach((item1) => {
            item1.noSearchMatch = false
          })
        })
        this.isSearchMatch = true
      }
    },

    // clear
    clearSearchText() {
      this.searchText = ''
      this.search()
    },

    /**
     * 防抖
     * @param fn            高频函数（要执行的函数）
     * @param wait          延时时间，毫秒
     * @param immediate     前置执行/后置执行，默认后置执行(false)
     */
    debounce(fn, wait, immediate) {
      let timer
      return function () {
        if (timer) clearTimeout(timer)
        if (immediate) {
          // 前置执行
          // 如果已经执行过，不再执行
          const callNow = !timer
          timer = setTimeout(() => {
            timer = null
          }, wait)
          if (callNow) {
            fn.apply(this, arguments)
          }
        } else {
          // 后置执行
          timer = setTimeout(() => {
            fn.apply(this, arguments)
          }, wait)
        }
      }
    }

  }
}
</script>

<style lang="scss" scoped>
@import '../../css/iconfont.css';

// #ifndef H5
// 处理 v-show 失效问题
::v-deep [hidden] {
  display: none !important;
}
// #endif

.classify__wrap {
  height: calc(100vh);
  /* #ifdef H5 */
  height: calc(100vh - var(--window-top));
  /* #endif */
  display: flex;
  flex-direction: column;
}

.classify__search-box {
  padding: 18rpx 30rpx;
  z-index: 9;
}

.classify__menu-wrap {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.classify__search-inner {
  background-color: rgb(234, 234, 234);
  border-radius: 100rpx;
  display: flex;
  align-items: center;
  padding: 10rpx 16rpx;

  .classify__nav-bar-input {
    margin-left: 8rpx;
    width: 100%;
    // #ifdef MP-ALIPAY
    background-color: rgb(234, 234, 234);
    // #endif
  }

  .classify__placeholder-class {
    font-size: 26rpx;
    color: #909399;
  }
}
.classify__clear-search {
  padding: 10rpx;
}

// #ifdef MP-ALIPAY
.classify__clear-search {
  width: 28rpx;
  height: 30rpx;
  font-size: 24rpx;
  background-color: #909399;
  color: #d0d1d3;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
}
// #endif

.classify__tab-view {
  width: 200rpx;
  height: 100%;
  // #ifdef MP-ALIPAY
  min-width: 160rpx;
  max-width: 160rpx;
  background-color: #ffffff;
  // #endif
}

.classify__tab-item {
  position: relative;
  height: 110rpx;
  background: #f6f6f6;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26rpx;
  color: #444;
  font-weight: 400;
  line-height: 1;
}

.classify__tab-item-active {
  position: relative;
  color: #000;
  font-size: 30rpx;
  font-weight: 600;
  background: #fff;
}

.classify__tab-item-active::before {
  content: "";
  position: absolute;
  border-left: 4px solid $uni-color-primary;
  height: 32rpx;
  left: 0;
  top: 39rpx;
}

.classify__line-1 {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.classify__right-box {
  background-color: rgb(250, 250, 250);

  // #ifdef MP-ALIPAY
  width: 100%;
  // #endif
}

.classify__page-view {
  padding: 16rpx;
}

.classify__class-item {
  margin-bottom: 30rpx;
  background-color: #fff;
  padding: 16rpx;
  border-radius: 8rpx;
}

.classify__class-item:last-child {
  min-height: 100vh;
}

.classify__item-title {
  font-size: 26rpx;
  color: $uni-text-color;
  font-weight: bold;
}

.classify__item-menu-name {
  font-weight: normal;
  font-size: 24rpx;
  color: $uni-text-color;
}

.classify__item-container {
  display: flex;
  flex-wrap: wrap;
}

.classify__thumb-box {
  position: relative;
  width: 33.333333%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  margin-top: 20rpx;
  //min-height: 78rpx;
}

.classify__item-menu-image {
  width: 120rpx;
  height: 120rpx;
  // #ifdef MP-ALIPAY
  background-size: cover;
  // #endif
}

.classify__badge {
  top: 0;
  right: 0;
  font-size: 18rpx;
  position: absolute;
  color: rgb(255, 255, 255);
  transform: translateY(0px) translateX(0px);
  background-color: #fa3534;
  display: flex;
  justify-content: center;
  align-items: center;
  line-height: 16rpx;
  padding: 5rpx 6rpx;
  border-radius: 72rpx;
}
.classify__click-class {
  opacity: 0.7;
}
.classify__selected-icon {
  color: #303133;
  font-size: 30rpx;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
}

.classify__empty {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;

  .classify__empty-icon {
    font-size: 200rpx;
  }
}

</style>
